package cn.bmbanjia.zebra2024.apps.meituanMiniApp.user.service.impl;

import cn.bmbanjia.zebra2024.apps.base.verificationCode.entity.VerificationCode;
import cn.bmbanjia.zebra2024.apps.base.verificationCode.service.IVerificationCodeService;
import cn.bmbanjia.zebra2024.apps.meituanMiniApp.satoken.MeituanCurrentUser;
import cn.bmbanjia.zebra2024.apps.meituanMiniApp.satoken.StpMeituanUtil;
import cn.bmbanjia.zebra2024.apps.meituanMiniApp.user.entity.MeituanMiniAppUser;
import cn.bmbanjia.zebra2024.apps.meituanMiniApp.user.mapper.MeituanMiniAppUserMapper;
import cn.bmbanjia.zebra2024.apps.meituanMiniApp.user.req.BindMobileReq;
import cn.bmbanjia.zebra2024.apps.meituanMiniApp.user.req.LoginReq;
import cn.bmbanjia.zebra2024.apps.meituanMiniApp.user.service.IMeituanMiniAppUserService;
import cn.bmbanjia.zebra2024.frame.exception.CodeException;
import cn.bmbanjia.zebra2024.frame.resp.Resp;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.util.UpdateEntity;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import io.github.linpeilie.Converter;
import io.netty.handler.codec.CodecException;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import static cn.bmbanjia.zebra2024.apps.base.verificationCode.entity.table.VerificationCodeTableDef.VERIFICATION_CODE;
import static cn.bmbanjia.zebra2024.apps.meituanMiniApp.user.entity.table.MeituanMiniAppUserTableDef.MEITUAN_MINI_APP_USER;

/**
 * 美团小程序用户 服务层实现。
 *
 * @author fugui
 * @since 2024-06-15
 */
@Slf4j
@Service
public class MeituanMiniAppUserServiceImpl extends ServiceImpl<MeituanMiniAppUserMapper, MeituanMiniAppUser> implements IMeituanMiniAppUserService {

    @Resource
    private Converter converter;
    @Resource
    private IVerificationCodeService verificationCodeService;

    /**
     * <a href="https://h5.dianping.com/app/gfe-docs/mt-miniprogram-onlinedoc/openapi/login/">...</a>
     *
     * @param req code
     * @return login status
     */
    @Override
    public synchronized Resp<Void> login(LoginReq req) {
        JSONObject param = JSONUtil.createObj()
                .set("appid", "appid")
                .set("secret", "secret")
                .set("js_code", req.getCode())
                .set("grant_type", "authorization_code");
        // 线上: https://openapi.meituan.com/mmp/open/auth/jscode2session
        // 线下：https://openapi.wpt.test.meituan.com/mmp/open/auth/jscode2session
        String resp = HttpUtil.post("https://openapi.meituan.com/mmp/open/auth/jscode2session", param.toString());
        log.info("resp={}", resp);
        JSONObject loginResp = JSONUtil.parseObj(resp);
        String openid = loginResp.getStr("openid");
        String unionid = loginResp.getStr("unionid");

        Assert.notBlank(openid, () -> new CodecException("openid is null"));

        MeituanCurrentUser user = getOneAs(QueryWrapper.create()
                .select(MEITUAN_MINI_APP_USER.USER_ID,
                        MEITUAN_MINI_APP_USER.USER_OPENID,
                        MEITUAN_MINI_APP_USER.USER_UNIONID,
                        MEITUAN_MINI_APP_USER.USER_NICK_NAME,
                        MEITUAN_MINI_APP_USER.USER_HEAD_URL,
                        MEITUAN_MINI_APP_USER.USER_MOBILE)
                .where(MEITUAN_MINI_APP_USER.USER_OPENID.eq(openid)), MeituanCurrentUser.class);

        if (ObjUtil.isNull(user)) {
            MeituanMiniAppUser build = MeituanMiniAppUser.builder()
                    .userNickName("小斑马" + RandomUtil.randomNumbers(4))
                    .userOpenid(openid)
                    .userUnionid(unionid)
                    .createUserId(0)
                    .createUserName(openid)
                    .build();
            boolean save = save(build);
            Assert.isTrue(save, CodeException::new);
            Assert.notNull(build.getUserId(), CodeException::new);
            user = converter.convert(build, MeituanCurrentUser.class);
        }

//        Assert.notNull(user, () -> new CodecException("MeituanMiniAppUser is null"));

        StpMeituanUtil.login(user);

        return Resp.success();
    }

    @Override
    @Transactional
    public synchronized Resp<Void> bindMobile(BindMobileReq req) {

        VerificationCode vc = verificationCodeService.getOne(QueryWrapper.create()
                .select(VERIFICATION_CODE.ID, VERIFICATION_CODE.CODE)
                .where(VERIFICATION_CODE.TELEPHONE.eq(req.getMobile()))
//                .where(VERIFICATION_CODE.CODE.eq(req.getCode()))
                .where(VERIFICATION_CODE.CREATE_TIME.ge(DateUtil.offsetMinute(DateUtil.date(), -5).getTime() / 1000))
                .orderBy(VERIFICATION_CODE.ID.desc())
                .limit(1));

        Assert.notNull(vc, () -> new CodecException("验证码错误"));
        Assert.isTrue(req.getCode().equals(vc.getCode()), () -> new CodecException("验证码错误"));

        MeituanCurrentUser meituanCurrentUser = StpMeituanUtil.currentUser();
        if (!req.getMobile().equals(meituanCurrentUser.getUserMobile())) {
            boolean b = updateById(MeituanMiniAppUser.builder()
                    .userId(meituanCurrentUser.getUserId())
                    .userMobile(req.getMobile())
                    .build());
            Assert.isTrue(b, CodeException::new);

            meituanCurrentUser.setUserMobile(req.getMobile());
            StpMeituanUtil.refreshCurrentUser(meituanCurrentUser);
        }

        boolean b = verificationCodeService.updateById(VerificationCode.builder()
                .id(vc.getId())
                .status(1)
                .updateTime((int) (DateUtil.date().getTime() / 1000))
                .updateUserId(meituanCurrentUser.getUserId())
                .updateUserName(meituanCurrentUser.getUserNickName())
                .build());
        Assert.isTrue(b, CodeException::new);

        return Resp.success();
    }

    @Override
    public Resp<Void> unbindMobile() {

        MeituanCurrentUser meituanCurrentUser = StpMeituanUtil.currentUser();
        MeituanMiniAppUser meituanMiniAppUser = UpdateEntity.of(MeituanMiniAppUser.class);
        meituanMiniAppUser.setUserId(meituanCurrentUser.getUserId());
        meituanMiniAppUser.setUserOpenid(null);
        boolean b = updateById(meituanMiniAppUser);
        Assert.isTrue(b, CodeException::new);

        meituanCurrentUser.setUserMobile(null);
        StpMeituanUtil.refreshCurrentUser(meituanCurrentUser);

        return Resp.success();
    }

}
